﻿using System;
using System.Collections.Generic;
using System.Text;
using DreamShield.Deployment.Miles.ApplicationUtils.ContextSwitch;
using DreamShield.UpdateCheckerApplication.Starter;
using System.Diagnostics;

namespace DreamShield.Deployment.Miles.ApplicationUtils
{
    #region Installed Infos Exceptions

    /// <summary>
    /// Exception levée lorsqu'il y a tentative d'ouverture d'informations
    /// concernant un produit qui n'est pas installé (ou pas avec DreamShield)
    /// </summary>
    [global::System.Serializable]
    public class ProductNotRegisteredException : Exception
    {
        /// <summary>
        /// Construit une nouvelle instance de la classe ProductNotRegisteredException
        /// </summary>
        public ProductNotRegisteredException() { }
        /// <summary>
        /// Construit une nouvelle instance de la classe ProductNotRegisteredException
        /// </summary>
        /// <param name="message"></param>
        public ProductNotRegisteredException(string message) : base(message) { }
        /// <summary>
        /// Construit une nouvelle instance de la classe ProductNotRegisteredException
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public ProductNotRegisteredException(string message, Exception inner) : base(message, inner) { }
        /// <summary>
        /// Construit une nouvelle instance de la classe ProductNotRegisteredException
        /// </summary>
        /// <param name="info"></param>
        /// <param name="context"></param>
        protected ProductNotRegisteredException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    /// <summary>
    /// Exception levée lorsqu'il y a tentative d'accès aux fonctions de mises à jour automatiques
    /// alors que l'application DreamShield's LiveUpdate n'est pas installée
    /// </summary>
    [global::System.Serializable]
    public class LiveUpdateApplicationNotInstalledException : Exception
    {
        /// <summary>
        /// Construit une nouvelle instance de la classe LiveUpdateApplicationNotInstalledException
        /// </summary>
        public LiveUpdateApplicationNotInstalledException() { }
        /// <summary>
        /// Construit une nouvelle instance de la classe LiveUpdateApplicationNotInstalledException
        /// </summary>
        /// <param name="message"></param>
        public LiveUpdateApplicationNotInstalledException(string message) : base(message) { }
        /// <summary>
        /// Construit une nouvelle instance de la classe LiveUpdateApplicationNotInstalledException
        /// </summary>
        /// <param name="message"></param>
        /// <param name="inner"></param>
        public LiveUpdateApplicationNotInstalledException(string message, Exception inner) : base(message, inner) { }
        /// <summary>
        /// Construit une nouvelle instance de la classe LiveUpdateApplicationNotInstalledException
        /// </summary>
        /// <param name="info"></param>
        /// <param name="context"></param>
        protected LiveUpdateApplicationNotInstalledException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    #endregion

    /// <summary>
    /// Classe qui permet d'obtenir les informations concernant l'application installée
    /// </summary>
    public class InstalledApplicationManager : MarshalByRefObject
    {
        #region Static Utils

        /// <summary>
        /// Indique si l'application ayant le guid spécifié est enregistrée (seules
        /// les installations DreamShield sont listées)
        /// </summary>
        /// <param name="appGuid">Guid de l'application recherchée</param>
        /// <returns>True si elle est installée, false sinon</returns>
        public static bool IsInstalledApplication(Guid appGuid)
        {
            return DreamShieldRuntimeConnect.Singleton.RuntimeEndPoint.IsInstalledApplication(appGuid);
        }

        /// <summary>
        /// Obtient la liste des applications installées avec DreamShield
        /// </summary>
        /// <returns>Liste des applications installées</returns>
        public static Guid[] GetInstalledApplications()
        {
            return DreamShieldRuntimeConnect.Singleton.RuntimeEndPoint.GetInstalledApplications();
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Construit une nouvelle instance de la classe <see cref="InstalledApplicationManager"/>
        /// </summary>
        /// <param name="installedApplication">Guid d'une application installée avec DreamShield</param>
        public InstalledApplicationManager(Guid installedApplication)
        {
            if (!IsInstalledApplication(installedApplication))
                throw new ProductNotRegisteredException(installedApplication.ToString());

            ProductGuid = installedApplication;

            RefreshPublicProperties();
        }

        /// <summary>
        /// Raffraîchit les informations du produit
        /// </summary>
        public virtual void Refresh()
        {
            RefreshPublicProperties();
        }

        /// <summary>
        /// Raffraîchit les informations du produit
        /// </summary>
        protected virtual void RefreshPublicProperties()
        {
            if (!IsInstalledApplication(ProductGuid))
                throw new ProductNotRegisteredException(ProductGuid.ToString());

            var infos = DreamShieldRuntimeConnect.Singleton.RuntimeEndPoint.RetrieveApplicationInformations(ProductGuid);

            ProductName = infos.name;
            ProductAuthor = infos.author;
            ProductCompany = infos.company;
            ProductDescription = infos.description;
            ProductTargetDir = infos.installationlocation;
            ProductUrl = infos.url;
            ProductVersion = infos.version;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Guid du produit
        /// </summary>
        public virtual Guid ProductGuid { get; protected set; }
        /// <summary>
        /// Nom du produit
        /// </summary>
        public virtual string ProductName { get; protected set; }
        /// <summary>
        /// Auteur du produit
        /// </summary>
        public virtual string ProductAuthor { get; protected set; }
        /// <summary>
        /// Companie du produit
        /// </summary>
        public virtual string ProductCompany { get; protected set; }
        /// <summary>
        /// Adresse internet du produit
        /// </summary>
        public virtual string ProductUrl { get; protected set; }
        /// <summary>
        /// Dossier dans lequel a été installé le produit
        /// </summary>
        public virtual string ProductTargetDir { get; protected set; }
        /// <summary>
        /// Description du produit
        /// </summary>
        public virtual string ProductDescription { get; protected set; }
        /// <summary>
        /// Version actuellement installée du produit
        /// </summary>
        public virtual Version ProductVersion { get; protected set; }

        /// <summary>
        /// Obtient la représentation textuelle de l'instance actuelle de InstalledApplicationManager
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return
                String.Format("Produit : {0}", ProductName) + "\n" +
                String.Format("Guid : {0}", ProductGuid) + "\n" +
                String.Format("Société : {0}", ProductCompany) + "\n" +
                String.Format("Url : {0}", ProductUrl) + "\n" +
                String.Format("Version : {0}", ProductVersion) + "\n" +
                String.Format("Installé à : {0}", ProductTargetDir);
        }

        #endregion

        #region Mises à jour

        /// <summary>
        /// Indique si les fonctions de mises à jour automatiques sont disponibles
        /// </summary>
        /// <remarks>Savoir si les mises à jour automatiques sont utilisables revient à vérifier
        /// si DreamShield's LiveUpdate version 2 ou plus est bien installé.
        /// Pour lier DreamShield's LiveUpdate a une application, il faut inclure
        /// le module partagé de DreamShield's LiveUpdate présent dans le dossier d'installation
        /// de DreamShield, sous dossier "Redist"</remarks>
        public static bool IsLiveUpdateAvailable
        {
            get
            {
                return GetInstalledLiveUpdateVersion() > 1;
                /* il faut avoir la version 2 minimum de DreamShield's LiveUpdate */
            }
        }

        /// <summary>
        /// Obtient la version installée de DreamShield's LiveUpdate, nécessaire à toutes les opérations
        /// de vérification des mises à jour
        /// </summary>
        /// <returns>0 si aucune version installée n'a été trouvée, la version sinon</returns>
        public static int GetInstalledLiveUpdateVersion()
        {
            return ApplicationStarter.GetInstalledLiveUpdateVersion();
        }

        /// <summary>
        /// Vérifie si le LiveUpdate est disponible, ou génère une exception sinon
        /// </summary>
        protected virtual void CheckLiveUpdateAvailability()
        {
            if (!IsLiveUpdateAvailable)
                throw new LiveUpdateApplicationNotInstalledException("La version 2 ou plus de DreamShield's LiveUpdate doit être installée pour réaliser cette action");
        }

        /// <summary>
        /// Indique si des mises à jour sont disponibles pour l'application
        /// </summary>
        /// <param name="publishUrl">Url de publication du produit (dans les options de publication du projet d'installation)</param>
        /// <param name="showCheckingToolTip">Indique si un message doit être affiché dans la zone de notification informant l'utilisateur
        /// qu'une opération de vérification des mises à jour est en cours sur son ordinateur</param>
        /// <returns>true si des mises à jour sont disponibles, false sinon</returns>
        public virtual bool HasUpdatesAvailable(string publishUrl, bool showCheckingToolTip)
        {
            CheckLiveUpdateAvailability();

            return ApplicationStarter.HasUpdateAvailable(ProductName, ProductGuid, publishUrl + "/publish.xml", showCheckingToolTip);
        }

        /// <summary>
        /// Si des mises à jour sont disponibles, propose à l'utilisateur
        /// une interface contenant les différentes mises à jour qu'il lui est possible d'installer
        /// et lui propose l'action d'installer les mises à jour sélectionnées
        /// </summary>
        /// <param name="publishUrl">Url de publication du produit (dans les options de publication du projet d'installation)</param>
        /// <returns>Processus ayant été crée pour la vérification des mises à jour</returns>
        public virtual Process ProposeUpdates(string publishUrl)
        {
            CheckLiveUpdateAvailability();

            ApplicationStarter.StarterOptions options = new ApplicationStarter.StarterOptions();
            options.AutoInstall = false;
            options.AutoStartChecking = true;
            options.ShowToolTip = false;
            options.Silent = false;

            return ApplicationStarter.ExecuteUpdateChecker(ProductName, ProductGuid,
                publishUrl + "/publish.xml", options);
        }

        /// <summary>
        /// Installe les mises à jour si des mises à jour sont disponibles.
        /// </summary>
        /// <param name="publishUrl">Url de publication du produit (dans les options de publication du projet d'installation)</param>
        /// <param name="showCheckingToolTip">Indique si un message doit être affiché dans la zone de notification informant l'utilisateur
        /// qu'une opération de vérification des mises à jour est en cours sur son ordinateur</param>
        /// <param name="silent">Indique si l'opération doit être silencieuse. Si c'est le cas, aucune fenêtre
        /// ne sera affichée à l'utilisateur en dehors des fenêtres éventuelles des assistants d'installation appelés. Sinon,
        /// la fenêtre du "vérificateur de mises à jour" sera affichée</param>
        /// <returns>Processus ayant été crée pour la vérification des mises à jour</returns>
        public virtual Process InstallUpdates(string publishUrl, bool showCheckingToolTip, bool silent)
        {
            CheckLiveUpdateAvailability();

            ApplicationStarter.StarterOptions options = new ApplicationStarter.StarterOptions();
            options.AutoInstall = true;
            options.AutoStartChecking = true;
            options.ShowToolTip = showCheckingToolTip;
            options.Silent = silent;

            return ApplicationStarter.ExecuteUpdateChecker(ProductName, ProductGuid,
                publishUrl + "/publish.xml", options);
        }

        #endregion
    }
}
